home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 6 code / TCP / NewsWatcher / NW Source / Source / subscribe.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-23  |  21.1 KB  |  828 lines  |  [TEXT/MMCC]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     subscribe.c
  4.  
  5.     This module handles subscribing and unsubscribing to groups.
  6.     
  7.     Copyright © 1994-1995, Northwestern University.
  8.  
  9. ----------------------------------------------------------------------------*/
  10.  
  11. #include <string.h>
  12. #include <stdio.h>
  13.  
  14. #include "glob.h"
  15. #include "dialog.h"
  16. #include "child.h"
  17. #include "newswatcher.h"
  18. #include "subscribe.h"
  19. #include "news.h"
  20. #include "memutil.h"
  21. #include "windutil.h"
  22. #include "listutil.h"
  23. #include "strutil.h"
  24. #include "group.h"
  25. #include "full.h"
  26.  
  27.  
  28.  
  29. #define kDuplicateGroupsAlert            151
  30.  
  31.  
  32.  
  33. /*----------------------------------------------------------------------------
  34.     PresentDuplicateGroupsAlert 
  35.     
  36.     Present the duplicate groups alert.
  37.     
  38.     Entry:    numDuplicates = number of duplicate groups.
  39.             dupNameOffset = offset in gGroupNames of duplicate group
  40.                 name if numDuplicates = 1.
  41.             drag = true if drag and drop, false if paste.
  42.                 
  43.     Exit:    function result = error code.
  44. ----------------------------------------------------------------------------*/
  45.  
  46. static OSErr PresentDuplicateGroupsAlert (short numDuplicates, 
  47.     long dupNameOffset, Boolean drag)
  48. {
  49.     CStr255 msg, fmt, groupName, movingOrPasting;
  50.     DialogPtr dlg;
  51.     short len, groupNameLen, item;
  52.     OSErr err = noErr;
  53.     
  54.     GetCString(drag ? kStrMoving : kStrPasting, movingOrPasting);
  55.     
  56.     if (numDuplicates > 1) {
  57.         GetCString(kStrDuplicateGroups, fmt);
  58.         sprintf(msg, fmt, movingOrPasting, movingOrPasting);
  59.     } else {
  60.         GetCString(kStrOneDuplicateGroup, fmt);
  61.         strcpy(groupName, *gGroupNames + dupNameOffset);
  62.         groupNameLen = strlen(groupName);
  63.         len = strlen(fmt) + groupNameLen + strlen(movingOrPasting) - 4;
  64.         if (len > 255) {
  65.             groupNameLen -= len - 255;
  66.             groupName[groupNameLen-3] = '.'; 
  67.             groupName[groupNameLen-2] = '.'; 
  68.             groupName[groupNameLen-1] = '.'; 
  69.             groupName[groupNameLen] = 0;
  70.         }
  71.         sprintf(msg, fmt, groupName, movingOrPasting);
  72.     }
  73.     c2pstr(msg);
  74.  
  75.     err = MyGetNewDialog(kDuplicateGroupsAlert, ok, cancel, &dlg);
  76.     if (err != noErr) return err;
  77.     ParamText((StringPtr)msg, "\p", "\p", "\p");
  78.     SysBeep(0);
  79.     MyModalDialog(dlg, gDialogFilterUPP, &item);
  80.     err = DoClose(dlg);
  81.     if (err != noErr) return err;
  82.     if (item == cancel) return userCanceledErr;
  83.     return noErr;
  84. }
  85.  
  86.  
  87.  
  88. /*----------------------------------------------------------------------------
  89.     ClearStatus 
  90.     
  91.     Clear the status fields in all groups in a window.
  92.     
  93.     Entry:    wind = pointer to group list window.
  94. ----------------------------------------------------------------------------*/
  95.  
  96. static void ClearStatus (WindowPtr wind)
  97. {
  98.     TWindow **info;
  99.     ListHandle theList;
  100.     TGroup **groupArray;
  101.     Cell theCell;
  102.     short cellDataLen, index, numGroups;
  103.     
  104.     info = (TWindow**)GetWRefCon(wind);
  105.     theList = (**info).theList;
  106.     groupArray = (**info).groupArray;
  107.     numGroups = (**theList).dataBounds.bottom;
  108.     theCell.h = 0;
  109.     for (theCell.v = 0; theCell.v < numGroups; theCell.v++) {
  110.         cellDataLen = 2;
  111.         LGetCell(&index, &cellDataLen, theCell, theList);
  112.         (*groupArray)[index].status = ' ';
  113.     }
  114. }
  115.  
  116.  
  117.  
  118. /*----------------------------------------------------------------------------
  119.     GroupInList 
  120.     
  121.     Check to see if a named group appears in a group list window, and
  122.     set the status field to 'x' if the group does appear.
  123.     
  124.     Entry:    nameOffset = offset in gGroupNames of group name.
  125.             wind = pointer to group list window.
  126.                 
  127.     Exit:    function result = true if group in window.
  128. ----------------------------------------------------------------------------*/
  129.  
  130. static Boolean GroupInList (long nameOffset, WindowPtr wind)
  131. {
  132.     TWindow **info;
  133.     ListHandle theList;
  134.     TGroup **groupArray;
  135.     Cell theCell;
  136.     short numGroups, cellDataLen, index;
  137.     
  138.     info = (TWindow**)GetWRefCon(wind);
  139.     theList = (**info).theList;
  140.     groupArray = (**info).groupArray;
  141.     numGroups = (**theList).dataBounds.bottom;
  142.     theCell.h = 0;
  143.     for (theCell.v = 0; theCell.v < numGroups; theCell.v++) {
  144.         cellDataLen = 2;
  145.         LGetCell(&index, &cellDataLen, theCell, theList);
  146.         if ((*groupArray)[index].nameOffset == nameOffset) {
  147.             (*groupArray)[index].status = 'x';
  148.             return true;
  149.         }
  150.     }
  151.     return false;
  152. }
  153.  
  154.  
  155.  
  156. /*----------------------------------------------------------------------------
  157.     RemoveDuplicates 
  158.     
  159.     Remove duplicate groups (the ones marked with status 'x').
  160.     
  161.     Entry:    wind = pointer to user group list window.
  162.     
  163.     Exit:    function result = error code.
  164. ----------------------------------------------------------------------------*/
  165.  
  166. static OSErr RemoveDuplicates (WindowPtr wind)
  167. {
  168.     WindowPtr child;
  169.     TWindow **info;
  170.     ListHandle theList;
  171.     TGroup **groupArray, groupInfo;
  172.     Cell theCell;
  173.     short numDel = 0;
  174.     GrafPtr port;
  175.     OSErr err = noErr;
  176.     short cellDataLen, index, numGroups;
  177.     
  178.     GetPort(&port);
  179.     SetPort(wind);
  180.     info = (TWindow**)GetWRefCon(wind);
  181.     theList = (**info).theList;
  182.     groupArray = (**info).groupArray;
  183.     numGroups = (**theList).dataBounds.bottom;
  184.     
  185.     LSetDrawingMode(false, theList);
  186.     theCell.h = 0;
  187.     for (theCell.v = 0; theCell.v < numGroups; theCell.v++) {
  188.         cellDataLen = 2;
  189.         LGetCell(&index, &cellDataLen, theCell, theList);
  190.         groupInfo = (*groupArray)[index];
  191.         if (groupInfo.status == 'x') {
  192.             if ((child = FindChild(wind, theCell)) != nil) {
  193.                 err = DoClose(child);
  194.                 if (err != noErr) goto exit;
  195.             }
  196.             DisposeGroupUnreadList(&groupInfo);
  197.             (*groupArray)[index] = groupInfo;
  198.             LDelRow(1, theCell.v, theList);
  199.             numDel++;
  200.             theCell.v--;
  201.             numGroups--;
  202.         }
  203.     }
  204.     LSetDrawingMode(true, theList);
  205.     SetPort(port);
  206.     return noErr;
  207.  
  208. exit:
  209.  
  210.     SetPort(port);
  211.     return err;
  212. }
  213.  
  214.  
  215.  
  216.  
  217. /*----------------------------------------------------------------------------
  218.     RedrawGroupCount 
  219.     
  220.     Redraw the count in the panel area of a group window.
  221.     
  222.     Entry:    wind = pointer to group window.
  223. ----------------------------------------------------------------------------*/
  224.  
  225. static void RedrawGroupCount (WindowPtr wind)
  226. {
  227.     GrafPtr port;
  228.     TWindow **info;
  229.     Rect r;
  230.  
  231.     GetPort(&port);
  232.     SetPort(wind);
  233.     info = (TWindow**)GetWRefCon(wind);
  234.     r = wind->portRect;
  235.     r.bottom = (**info).panelHeight-3;
  236.     InvalRect(&r);
  237.     SetPort(port);
  238. }
  239.  
  240.  
  241.  
  242. /*----------------------------------------------------------------------------
  243.     RemoveGroupFromUnsubscribedList 
  244.     
  245.     Remove a group from the unsubscribed list.
  246.     
  247.     Entry:    groupName = group name.
  248.             unsubscribed = handle to unsubscribed list.
  249. ----------------------------------------------------------------------------*/
  250.  
  251. void RemoveGroupFromUnsubscribedList (char *groupName, Handle unsubscribed)
  252. {
  253.     long len, unsubscribedLen;
  254.     char *p, *pEnd, *q;
  255.  
  256.     if (unsubscribed == nil) return;
  257.     len = strlen(groupName);
  258.     unsubscribedLen = MyGetHandleSize(unsubscribed);
  259.     p = *unsubscribed;
  260.     pEnd = p + unsubscribedLen;
  261.     while (p < pEnd) {
  262.         q = p;
  263.         while (q < pEnd && *q != CR) q++;
  264.         q++;
  265.         if (MyStrNEqual(p, groupName, len)) break;
  266.         p = q;
  267.     }
  268.     if (p > pEnd) return;
  269.     len = q-p;
  270.     BlockMoveData(q, p, pEnd-q);
  271.     MySetHandleSize(unsubscribed, unsubscribedLen - len);
  272. }
  273.  
  274.  
  275.  
  276. /*----------------------------------------------------------------------------
  277.     AddNewGroup 
  278.     
  279.     Add a new group to a user group window.
  280.     
  281.     Entry:    nameOffset = offset in gGroupNames of group name to add.
  282.             wind = pointer to user group window.
  283.             pos = position of new user group list entry (row number
  284.                 of new cell). Pass 0x7fff to add at end of list.
  285.             theGroup = pointer to group info to add, or nil to get info
  286.                 for new group from server.
  287.             cloneUnreadList = true to clone a copy of the unread list
  288.                 in theGroup.
  289.  
  290.     Exit:    function result = error code.
  291. ----------------------------------------------------------------------------*/
  292.  
  293. OSErr AddNewGroup (long nameOffset, WindowPtr wind, short pos, 
  294.     TGroup *theGroup, Boolean cloneUnreadList)
  295. {
  296.     ListHandle theList;
  297.     Cell theCell;
  298.     TWindow **info;
  299.     TGroup **groupArray, newGroup;
  300.     short newGroupIndex;
  301.     OSErr err = noErr;
  302.     Boolean groupExists;
  303.     GrafPtr port;
  304.     TUnread **unread, **newUnread, **prevNewUnread;
  305.     CStr255 groupName;
  306.     
  307.     GetPort(&port);
  308.     
  309.     newGroup.firstMess = 1;
  310.     newGroup.lastMess = 0;
  311.     newGroup.numUnread = 0;
  312.     newGroup.unread = nil;
  313.     newGroup.status = ' ';
  314.  
  315.     info = (TWindow**)GetWRefCon(wind);
  316.     theList = (**info).theList;
  317.     groupArray = (**info).groupArray;
  318.     strcpy(groupName, *gGroupNames + nameOffset);
  319.     
  320.     if (theGroup == nil) {
  321.         newGroup.nameOffset = nameOffset;
  322.         newGroup.onlyRedrawCount = false;
  323.         err = GetGroupArticleRange(&newGroup, &groupExists);
  324.         if (err != noErr) goto exit;
  325.         if (groupExists && newGroup.firstMess <= newGroup.lastMess) {
  326.             err = MyNewHandle(sizeof(TUnread), &unread);
  327.             if (err != noErr) goto exit;
  328.             (**unread).firstUnread = newGroup.firstMess;
  329.             (**unread).lastUnread = newGroup.lastMess;
  330.             (**unread).next = nil;
  331.             newGroup.unread = unread;
  332.         }
  333.     } else {
  334.         newGroup = *theGroup;
  335.         if (cloneUnreadList) {
  336.             newGroup.unread = nil;
  337.             unread = theGroup->unread;
  338.             prevNewUnread = nil;
  339.             while (unread != nil) {
  340.                 err = MyNewHandle(sizeof(TUnread), &newUnread);
  341.                 if (err != noErr) goto exit;
  342.                 **newUnread = **unread;
  343.                 (**newUnread).next = nil;
  344.                 if (prevNewUnread == nil) {
  345.                     newGroup.unread = newUnread;
  346.                 } else {
  347.                     (**prevNewUnread).next = newUnread;
  348.                 }
  349.                 prevNewUnread = newUnread;
  350.                 unread = (**unread).next;
  351.             }
  352.         }
  353.     }
  354.  
  355.     err = MySetHandleSize(groupArray, MyGetHandleSize(groupArray) + sizeof(TGroup));
  356.     if (err != noErr) goto exit;
  357.     newGroupIndex = (**info).numGroups;
  358.     (**info).numGroups++;
  359.     (*groupArray)[newGroupIndex] = newGroup;
  360.  
  361.     LSetDrawingMode(false, theList);
  362.     pos = LAddRow(1, pos, theList);
  363.     SetPt(&theCell, 0, pos);
  364.     LSetCell(&newGroupIndex, 2, theCell, theList);
  365.     LSetDrawingMode(true, theList);
  366.     
  367.     RemoveGroupFromUnsubscribedList(groupName, (**info).unsubscribed);
  368.  
  369.     SetPort(wind);
  370.     InvalRect(&wind->portRect);
  371.     
  372.     (**info).changed = true;
  373.     SetPort(port);
  374.     return noErr;
  375.     
  376. exit:
  377.  
  378.     SetPort(port);
  379.     DisposeGroupUnreadList(&newGroup);
  380.     return err;
  381. }
  382.  
  383.  
  384.  
  385. /*----------------------------------------------------------------------------
  386.     CopyGroupsFromScrap 
  387.     
  388.     Copy groups from scrap data to a user group list window.
  389.     
  390.     Entry:    data = handle to scrap data. WARNING: The scrap data is
  391.                 modified by this function.
  392.             wind = pointer to destination user group list window.
  393.             pos = position of new user group list entries in destination
  394.                 window (starting row number of new cells). 
  395.                 Pass 0x7fff to add at end of list.
  396.                 
  397.     Exit:    function result = error code.
  398. ----------------------------------------------------------------------------*/
  399.  
  400. OSErr CopyGroupsFromScrap (Handle data, WindowPtr wind, short pos)
  401. {
  402.     TWindow **info;
  403.     ListHandle theList;
  404.     long dataPos, groupNameLen, nameOffset, numUnreadPairs;
  405.     long numGroups, i, dupNameOffset;
  406.     short firstPos;
  407.     CStr255 groupName;
  408.     TGroup theGroup;
  409.     TUnread **prevUnread, **unread;
  410.     OSErr err = noErr;
  411.     short numSubscribed, index, numCells;
  412.     short numDuplicates, numNotInFullGroupList;
  413.     Cell theCell;
  414.     GrafPtr port;
  415.     Rect r;
  416.     long j, jDataPos, jGroupNameLen;
  417.     
  418.     GetPort(&port);
  419.     SetPort(wind);
  420.     
  421.     theGroup.unread = nil;
  422.     
  423.     err = CheckScrapData(data);
  424.     if (err != noErr) goto exit;
  425.     
  426.     info = (TWindow**)GetWRefCon(wind);
  427.     theList = (**info).theList;
  428.     numCells = (**theList).dataBounds.bottom;
  429.     if (pos > numCells) pos = numCells;
  430.     
  431.     dataPos = 12;
  432.     numGroups = *(long*)(*data + dataPos);
  433.     dataPos += sizeof(long);
  434.     
  435.     ClearStatus(wind);
  436.     
  437.     /* Check for duplicate groups, both duplicates within the scrap data
  438.        and duplicates between the scrap data and the target window. Also 
  439.        check for groups which no longer exist in the full group list.
  440.        
  441.        For each group in the scrap data, the byte immediately following 
  442.        the group name length byte is set to 0 to skip the group, either 
  443.        because it is a duplicate within the scrap data or because it no 
  444.        longer exists in the full group list. */
  445.     
  446.     numDuplicates = 0;
  447.     numNotInFullGroupList = 0;
  448.     for (i = 0; i < numGroups; i++) {
  449.         groupNameLen = *(unsigned char*)(*data + dataPos);
  450.         dataPos++;
  451.         BlockMoveData(*data + dataPos, groupName, groupNameLen);
  452.         *(groupName + groupNameLen) = 0;
  453.         index = FindGroupIndex(groupName);
  454.         if (index == -1) {
  455.             nameOffset = -1;
  456.             numNotInFullGroupList++;
  457.         } else {
  458.             nameOffset = (*gFullGroupArray)[index].nameOffset;
  459.             for (j = 0, jDataPos = 16; j < i; j++) {
  460.                 jGroupNameLen = *(unsigned char*)(*data + jDataPos);
  461.                 jDataPos++;
  462.                 if (jGroupNameLen == groupNameLen &&
  463.                     strncmp(*data + jDataPos, groupName, groupNameLen) == 0)
  464.                 {
  465.                     nameOffset = -1;
  466.                     break;
  467.                 }
  468.                 jDataPos += jGroupNameLen;
  469.                 jDataPos = ((jDataPos + 3) >> 2) << 2;
  470.                 jDataPos += 3*sizeof(long);
  471.                 numUnreadPairs = *(long*)(*data + jDataPos);
  472.                 jDataPos += sizeof(long);
  473.                 jDataPos += numUnreadPairs * 2 * sizeof(long);
  474.             }
  475.             if (nameOffset >= 0 && GroupInList(nameOffset, wind)) {
  476.                 dupNameOffset = nameOffset;
  477.                 numDuplicates++;
  478.             }
  479.         }
  480.         if (nameOffset == -1) *(*data + dataPos) = 0;
  481.         dataPos += groupNameLen;
  482.         dataPos = ((dataPos + 3) >> 2) << 2;
  483.         dataPos += 3*sizeof(long);
  484.         numUnreadPairs = *(long*)(*data + dataPos);
  485.         dataPos += sizeof(long);
  486.         dataPos += numUnreadPairs * 2 * sizeof(long);
  487.     }
  488.     
  489.     if (numDuplicates > 0) {
  490.         err = PresentDuplicateGroupsAlert(numDuplicates, dupNameOffset, true);
  491.         if (err != noErr) goto exit;
  492.     }
  493.     
  494.     dataPos = 16;
  495.     firstPos = pos;
  496.     numSubscribed = 0;
  497.     for (i = 0; i < numGroups; i++) {
  498.         groupNameLen = *(unsigned char*)(*data + dataPos);
  499.         dataPos++;
  500.         BlockMoveData(*data + dataPos, groupName, groupNameLen);
  501.         dataPos += groupNameLen;
  502.         dataPos = ((dataPos + 3) >> 2) << 2;
  503.         if (*groupName == 0) {
  504.             dataPos += 3*sizeof(long);
  505.             numUnreadPairs = *(long*)(*data + dataPos);
  506.             dataPos += sizeof(long);
  507.             dataPos += numUnreadPairs * 2 * sizeof(long);
  508.         } else {
  509.             *(groupName + groupNameLen) = 0;
  510.             index = FindGroupIndex(groupName);
  511.             nameOffset = (*gFullGroupArray)[index].nameOffset;
  512.             theGroup.nameOffset = nameOffset;
  513.             theGroup.firstMess = *(long*)(*data + dataPos);
  514.             dataPos += sizeof(long);
  515.             theGroup.lastMess = *(long*)(*data + dataPos);
  516.             dataPos += sizeof(long);
  517.             theGroup.numUnread = *(long*)(*data + dataPos);
  518.             dataPos += sizeof(long);
  519.             theGroup.onlyRedrawCount = false;
  520.             numUnreadPairs = *(long*)(*data + dataPos);
  521.             dataPos += sizeof(long);
  522.             prevUnread = nil;
  523.             while (numUnreadPairs--) {
  524.                 err = MyNewHandle(sizeof(TUnread), &unread);
  525.                 if (err != noErr) goto exit;
  526.                 (**unread).firstUnread = *(long*)(*data + dataPos);
  527.                 dataPos += sizeof(long);
  528.                 (**unread).lastUnread = *(long*)(*data + dataPos);
  529.                 dataPos += sizeof(long);
  530.                 (**unread).next = nil;
  531.                 if (prevUnread == nil) {
  532.                     theGroup.unread = unread;
  533.                 } else {
  534.                     (**prevUnread).next = unread;
  535.                 }
  536.                 prevUnread = unread;
  537.             }
  538.             if (theGroup.firstMess >= 0) {
  539.                 err = AddNewGroup(nameOffset, wind, pos, &theGroup, false);
  540.                 if (err != noErr) goto exit;
  541.                 theGroup.unread = nil;
  542.             } else {
  543.                 err = AddNewGroup(nameOffset, wind, pos, nil, false);
  544.                 if (err != noErr) goto exit;
  545.                 theGroup.unread = nil;
  546.             }
  547.             pos++;
  548.             numSubscribed++;
  549.         }
  550.     }
  551.     
  552.     if (numNotInFullGroupList) NoteMessageNumber(kStrSomeGroupsNotInFullGroupList);
  553.     
  554.     if (numSubscribed == 0) goto exit;
  555.     
  556.     SelectOrDeselectAllListItems(theList, false);
  557.     theCell.h = 0;
  558.     for (theCell.v = firstPos; theCell.v < firstPos + numSubscribed; theCell.v++)
  559.         MyLSetSelect(true, theCell, theList);
  560.         
  561.     if (numDuplicates > 0) {
  562.         err = RemoveDuplicates(wind);
  563.         if (err != noErr) goto exit;
  564.     }
  565.     
  566.     MyLScroll(0, theList);
  567.         
  568.     if (gPrefs.reZoomWindows) {
  569.         err = DoZoom(wind, inZoomOut);
  570.         if (err != noErr) goto exit;
  571.     } else {
  572.         SetWindowNeedsZooming(wind);
  573.     }
  574.     RedrawGroupCount(wind);
  575.     
  576.     r = (**theList).rView;
  577.     InvalRect(&r);
  578.     
  579.     SetPort(port);
  580.     return noErr;
  581.     
  582. exit:
  583.  
  584.     SetPort(port);
  585.     DisposeGroupUnreadList(&theGroup);
  586.     return err;
  587. }
  588.  
  589.  
  590.  
  591. /*----------------------------------------------------------------------------
  592.     CopyOrMoveSelectedGroups 
  593.     
  594.     Copy or move selected groups from a group list window to a user group
  595.     list window.
  596.     
  597.     Entry:    srcWindow = pointer to source group list window.
  598.             destWindow = pointer to destination user group list window.
  599.             pos = position of new user group list entries in destination
  600.                 window (starting row number of new cells). 
  601.                 Pass 0x7fff to add at end of list.
  602.             copy = true to copy groups, false to move groups.
  603.                 
  604.     Exit:    function result = error code.
  605. ----------------------------------------------------------------------------*/
  606.  
  607. OSErr CopyOrMoveSelectedGroups (WindowPtr srcWindow, WindowPtr destWindow, short pos,
  608.     Boolean copy)
  609. {
  610.     Cell srcCell, destCell;
  611.     TWindow **srcInfo, **destInfo;
  612.     TGroup **srcGroupArray, srcGroup;
  613.     ListHandle srcList, destList;
  614.     short index, cellDataLen;
  615.     short numSelected=0, firstPos, numGroups;
  616.     OSErr err = noErr;
  617.     TGroupWindowKind srcKind;
  618.     GrafPtr port;
  619.     short numDuplicates;
  620.     long nameOffset, dupNameOffset;
  621.     Rect r;
  622.     
  623.     GetPort(&port);
  624.     SetPort(destWindow);
  625.     
  626.     srcInfo = (TWindow**)GetWRefCon(srcWindow);
  627.     srcList = (**srcInfo).theList;
  628.     srcGroupArray = (**srcInfo).groupArray;
  629.     srcKind = (**srcInfo).groupKind;
  630.     destInfo = (TWindow**)GetWRefCon(destWindow);
  631.     destList = (**destInfo).theList;
  632.     numGroups = (**destList).dataBounds.bottom;
  633.     if (pos > numGroups) pos = numGroups;
  634.     
  635.     ClearStatus(destWindow);
  636.     
  637.     SetPt(&srcCell,0,0);
  638.     numDuplicates = 0;
  639.     while (LGetSelect(true, &srcCell, srcList)) {
  640.         cellDataLen = 2;
  641.         LGetCell(&index, &cellDataLen, srcCell, srcList);
  642.         nameOffset = (*srcGroupArray)[index].nameOffset;
  643.         if (GroupInList(nameOffset, destWindow)) {
  644.             dupNameOffset = nameOffset;
  645.             numDuplicates++;
  646.         }
  647.         srcCell.v++;
  648.     }
  649.     
  650.     if (numDuplicates > 0) {
  651.         err = PresentDuplicateGroupsAlert(numDuplicates, dupNameOffset, true);
  652.         if (err != noErr) goto exit;
  653.     }
  654.     
  655.     SetPt(&srcCell,0,0);
  656.     firstPos = pos;
  657.     while (LGetSelect(true, &srcCell, srcList)) {
  658.         numSelected++;
  659.         cellDataLen = 2;
  660.         LGetCell(&index, &cellDataLen, srcCell, srcList);
  661.         if (srcKind == kUserGroup) {
  662.             srcGroup = (*srcGroupArray)[index];
  663.             err = AddNewGroup(srcGroup.nameOffset, destWindow, pos,
  664.                 &srcGroup, copy);
  665.             if (err != noErr) goto exit;
  666.             if (!copy) {
  667.                 srcGroup.unread = nil;
  668.                 (*srcGroupArray)[index] = srcGroup;
  669.             }
  670.         } else {
  671.             err = AddNewGroup((*srcGroupArray)[index].nameOffset, destWindow, pos,
  672.                 nil, false);
  673.             if (err != noErr) goto exit;
  674.         }
  675.         srcCell.v++;
  676.         pos++;
  677.     }
  678.     
  679.     if (numSelected == 0) goto exit;
  680.     
  681.     if (!copy) {
  682.         err = UnsubscribeSelected(srcWindow);
  683.         if (err != noErr) goto exit;
  684.     }
  685.     
  686.     SelectOrDeselectAllListItems(destList, false);
  687.     destCell.h = 0;
  688.     for (destCell.v = firstPos; destCell.v < firstPos + numSelected; destCell.v++)
  689.         MyLSetSelect(true, destCell, destList);
  690.         
  691.     if (numDuplicates > 0) {
  692.         err = RemoveDuplicates(destWindow);
  693.         if (err != noErr) goto exit;
  694.     }
  695.     
  696.     MyLScroll(0, destList);
  697.         
  698.     if (gPrefs.reZoomWindows) {
  699.         err = DoZoom(destWindow, inZoomOut);
  700.         if (err != noErr) goto exit;
  701.     } else {
  702.         SetWindowNeedsZooming(destWindow);
  703.     }
  704.     RedrawGroupCount(destWindow);
  705.     
  706.     r = (**destList).rView;
  707.     InvalRect(&r);
  708.     
  709.     SetPort(port);
  710.     return noErr;
  711.     
  712. exit:
  713.  
  714.     SetPort(port);
  715.     return err;
  716. }
  717.  
  718.  
  719.  
  720. /*----------------------------------------------------------------------------
  721.     AddGroupToUnsubscribedList 
  722.     
  723.     Add a group to the unsubscribed list.
  724.     
  725.     Entry:    groupName = group name.
  726.             unsubscribed = handle to unsubscribed list.
  727.             
  728.     Exit:    function result = error code.
  729. ----------------------------------------------------------------------------*/
  730.  
  731. OSErr AddGroupToUnsubscribedList (char *groupName, Handle unsubscribed)
  732. {
  733.     long len, unsubscribedLen;
  734.     char *p, *pEnd, *q;
  735.     OSErr err = noErr;
  736.  
  737.     if (unsubscribed == nil) return noErr;
  738.     len = strlen(groupName);
  739.     unsubscribedLen = MyGetHandleSize(unsubscribed);
  740.     p = *unsubscribed;
  741.     pEnd = p + unsubscribedLen;
  742.     while (p < pEnd) {
  743.         q = p;
  744.         while (q < pEnd && *q != CR) q++;
  745.         q++;
  746.         if (MyStrNEqual(p, groupName, len)) break;
  747.         p = q;
  748.     }
  749.     if (p < pEnd) return noErr;
  750.     err = MySetHandleSize(unsubscribed, unsubscribedLen + len + 2);
  751.     if (err != noErr) return err;
  752.     p = *unsubscribed + unsubscribedLen;
  753.     BlockMoveData(groupName, p, len);
  754.     p += len;
  755.     *p++ = '!';
  756.     *p++ = CR;
  757.     return noErr;
  758. }
  759.  
  760.  
  761.  
  762. /*----------------------------------------------------------------------------
  763.     UnsubscribeSelected 
  764.     
  765.     Unsubscribe selected newsgroups.
  766.     
  767.     Entry:    wind = pointer to user group list window.
  768.     
  769.     Exit:    function result = error code.
  770. ----------------------------------------------------------------------------*/
  771.  
  772. OSErr UnsubscribeSelected (WindowPtr wind)
  773. {
  774.     WindowPtr child;
  775.     TWindow **info;
  776.     ListHandle theList;
  777.     TGroup **groupArray, groupInfo;
  778.     Cell theCell;
  779.     short numDel = 0;
  780.     GrafPtr port;
  781.     Rect r;
  782.     OSErr err = noErr;
  783.     short cellDataLen, index;
  784.     CStr255 groupName;
  785.     
  786.     GetPort(&port);
  787.     SetPort(wind);
  788.     info = (TWindow**)GetWRefCon(wind);
  789.     theList = (**info).theList;
  790.     groupArray = (**info).groupArray;
  791.     
  792.     LSetDrawingMode(false, theList);
  793.     SetPt(&theCell,0,0);
  794.     while (LGetSelect(true, &theCell, theList)) {
  795.         cellDataLen = 2;
  796.         LGetCell(&index, &cellDataLen, theCell, theList);
  797.         if ((child = FindChild(wind, theCell)) != nil) {
  798.             err = DoClose(child);
  799.             if (err != noErr) goto exit;
  800.         }
  801.         groupInfo = (*groupArray)[index];
  802.         DisposeGroupUnreadList(&groupInfo);
  803.         (*groupArray)[index] = groupInfo;
  804.         strcpy(groupName, *gGroupNames + groupInfo.nameOffset);
  805.         err = AddGroupToUnsubscribedList(groupName, (**info).unsubscribed);
  806.         if (err != noErr) goto exit;
  807.         LDelRow(1, theCell.v, theList);
  808.         numDel++;
  809.     }
  810.     LSetDrawingMode(true, theList);
  811.     if (numDel > 0) {
  812.         (**info).changed = true;
  813.         MyLScroll(0, theList);
  814.         r = (**theList).rView;
  815.         InvalRect(&r);
  816.         RedrawGroupCount(wind);
  817.         SetWindowNeedsZooming(wind);
  818.     }
  819.     SetPort(port);
  820.     return noErr;
  821.  
  822. exit:
  823.  
  824.     SetPort(port);
  825.     return err;
  826. }
  827.  
  828.